#include <math.h>
#include <iostream>
#include <functional>

using std::cout;
using std::endl;
using std::bind;
using std::function;

//面向对象的写法：继承 + 虚函数(纯虚函数)(运行)
//基于对象的写法：std::bind + std::function(编译)，也能体现多态

//“基类”类体内 分4步：
//1、using接收
//2、声明回调对象（数据成员）
//3、注册函数（成员函数）
//4、执行函数（成员函数）

//“基类”类体外 分5步：
//1、实现要绑定的回调函数
//2、创建回调函数所在类的对象，用于绑定，此外没有什么用
//3、创建使用回调函数的类的对象，用于调用
//4、调用注册函数，
//5、调用执行函数

//面向对象在重写时要求保持一致，效率也没有基于对象高，但灵活性高

class Figure
{
public:
    //1、使用function接收函数类型
    using DisplayCallback = function<void()>;
    using AreaCallback = function<double()>;

    //2、声明回调对象
    DisplayCallback _displayCallback;
    AreaCallback _areaCallback;

    //3、回调函数的注册。注册函数会向外提供
    //其实就是把左值cb赋值给callback对象
#if 0
    void setDisplayCallback(const DisplayCallback &cb)
    {
        _displayCallback = cb;
    }
    //只要能保证传递进来的是右值，那么const左值引用的写法用不到
#endif
    void setDisplayCallback(DisplayCallback &&cb)
    {
        _displayCallback = std::move(cb);   //保证执行的是移动赋值函数
    }

    void setAreaCallback(AreaCallback &&cb)
    {
        _areaCallback = std::move(cb);
    }

    //4、回调函数的执行。执行函数会向外提供
    //其实就是执行传进来的cb
    //注意返回类型要和里面执行的cb一致
    void handleDisplayCallback() const
    {
        if(_displayCallback)
        {
            _displayCallback();
        }
    }

    double handleAreaCallback() const
    {
        if(_areaCallback)
        {
            return _areaCallback();
        }
        else
        {
            return 0.0;
        }
    }

};

class Rectangle
{
public:
    Rectangle(double length = 0, double width = 0)
    : _length(length)
    , _width(width)
    {
        cout << "Rectangle(double = 0, double = 0)" << endl;
    }

    void display(int x) const 
    {
        cout << "Rectangle";
    }

    double area() const 
    {
        return _length * _width;
    }

    ~Rectangle()
    {
        cout << "~Rectangle()" << endl;
    }
private:
    double _length;
    double _width;
};

class Circle
{
public:
    Circle(double radius = 0)
    : _radius(radius)
    {
        cout << "Circle(double = 0)" << endl;
    }

    void print() const 
    {
        cout << "Circle";
    }

    double printArea() const 
    {
        return 3.14 * _radius *_radius;;
    }

    ~Circle()
    {
        cout << "~Circle()" << endl;
    }
private:
    double _radius;
};

class Triangle
{
public:
    Triangle(double a = 0, double b = 0, double c = 0)
    : _a(a)
    , _b(b)
    , _c(c)
    {
        cout << "Triangle(double = 0, double = 0, double = 0)" << endl;
    }

    void show() const 
    {
        cout << "Triangle";
    }

    double showArea() const 
    {
        //海伦公式
        double tmp = (_a + _b + _c)/2;

        return sqrt(tmp * (tmp - _a) * (tmp - _b) * (tmp - _c));
    }

    ~Triangle()
    {
        cout << "~Triangle()" << endl;
    }
private:
    double _a;
    double _b;
    double _c;
};

void func(const Figure &fig)
{
    fig.handleDisplayCallback();
    cout << "的面积 : " << fig.handleAreaCallback() << endl;
}

int main()
{
    //2、创建所在类对象，用于绑定
    //为什么要创建？直接把要绑定的函数设为static不就行了吗？
    //不行，因为static函数只能访问静态数据成员。
    Rectangle rectangle(10, 20);
    Circle circle(10);
    Triangle triangle(3, 4, 5);

    cout << endl;
    Figure fig;//3、创建使用类对象，用于调用
    fig.setDisplayCallback(bind(&Rectangle::display, &rectangle, 10));//4、注册
    fig.setAreaCallback(bind(&Rectangle::area, &rectangle));//4、注册
    func(fig);//5、执行

    cout << endl;
    fig.setDisplayCallback(bind(&Circle::print, &circle));
    //fig.setDisplayCallback(bind(circle.print(), &circle));
    //bind绑定成员函数时，第一个参数应该是指向对象的指针或引用，而不是直接调用成员函数。
    fig.setAreaCallback(bind(&Circle::printArea, &circle));
    func(fig);

    cout << endl;
    fig.setDisplayCallback(bind(&Triangle::show, &triangle));
    fig.setAreaCallback(bind(&Triangle::showArea, &triangle));
    func(fig);
    cout << endl;

    return 0;
}

